package GTGExpession;
import java.util.*;

import bexpred.*;

/**
 * Class for keeping expression for GTGAlgorithm (as a list of XOR statements)
 * @author Tomasz Werszko
 * @author Rafal Magda
 */
public class GTGExpression extends RM{
	private GTGXor xors = new GTGXor();
	public int k;
	
	public GTGExpression(){
		super();
		xors = new GTGXor();
	}
	
	public GTGExpression(String expression) throws BExprException{
		super(expression);
		this.processRM();
	}
	
	/**
	 * Constructor which constructs Reed - Muller expression and stores it for further operations
	 * @param bexprtree - expression tree
	 * throws Exception
	 */
	public GTGExpression(BExprTree bexprtree) throws BExprException{
		super(bexprtree);
		this.processRM();
	}
	
	/**
	 * Constructor which constructs Reed - Muller expression and stores it for further operations
	 * @param truthTable
	 * @param varInputs
	 * @throws Exception
	 * 
	 */
	public GTGExpression(Boolean truthTable[][], List<String> varInputs) throws Exception{
		super(truthTable, varInputs);
		this.processRM();
		
		
	}
	
	/**
	 * Processes data created by super() constructor
	 */
	protected void processRM(){
		//checking all rows of the truth table
		for(int i = 0; i < this.rows; i++){
			Boolean[] vector = this.truthTable[i];
			//if all inputs are false
			if(i == 0 && this.RMFactors[i] == true){
				//String string = "1";
				//GTGOr or = new GTGOr();
				//GTGAnd and = new GTGAnd();
				//and.variables.add(string);
				//or.ands.add(and);	// 1 element in ors
				//this.xors.ors.add(or);
			}
			//if not all inputs are false
			else if(this.RMFactors[i] == true){
				GTGOr or = new GTGOr();
				GTGAnd and = new GTGAnd();
				for(int j = 0; j < (vector.length - 1); j++){
					if(vector[j] == true){
						and.variables.add(this.varInputs.get(j));
					}
				}
				or.ands.add(and);	// 1 element in ors
				this.xors.ors.add(or);
			}
		}
	}
	
	/**
	 * Prints truth table to the console
	 */
	public void printTruthTable(){
		//variables
	    System.out.println("Variables:");
	    
	    Iterator<String> iterator = this.varInputs.iterator();
	    while(iterator.hasNext()){
	    	String element = iterator.next();
	    	System.out.print(element + "\t");
	    }
	    System.out.println();
		//printing truth table
	    System.out.println("Truth table");
		for(int i = 0; i < this.truthTable.length; i++){
	    	for(int j = 0; j < this.truthTable[i].length; j++){
	    		System.out.print(this.truthTable[i][j] + "\t");
	    	}
	    	System.out.println();
	    }
	}
	
	/**
	 * Returns readable form of GTGExpression expression
	 * @return readable form of boolean expression
	 */
	public String toString(){
		return xors.toString();
	}

	/**
	 * Returns readable form of GTGExpression expression as an output of synthesis
	 * @return readable form of boolean expression
	 */
	public String toOutputString(){
		String str = "";
		for(int i=0; i<xors.ors.size(); i++){
			str+="N_"+i+" = "+xors.ors.get(i).toString()+"\n";
		}
		return str;
	}
	
	
	/**
	 * @return string representation of R-M expression stored in this object
	 */
	public String RMtoString(){
		return super.toString();
	}
	
	/**
	 * Synthesize GTGExpression version
	 * @param verbose - If 1 additional output is turned on. If -1 - no output is printed.
	 * @return GTGExpression in synthesized form
	 */
	public GTGExpression synthesize(int verbose){
		GTGExpression expr = this;
		int i_count=0;
		boolean nextloop=true;
		
		// check if we have something to do
		if(xors.ors.size()==0){
			return expr;
		}
		
		// step 1
		// Transform input expression to Reed-Muller canonical form
		// It must be done previously
		// Logical "0" and "1" are eliminated previously, too.
		
		// step 2,3
		// if Y (0n) = 0 then NDRl > NDRd
		BExprTree bexprtree = null;
		try {
			bexprtree = new BExprTree(expr.toString());
		} catch (BExprPreParseException e) {
			e.printStackTrace();
		}
		int vars = bexprtree.getVarCount();
		// vector, which contain only logical "0"
		boolean falses[] = new boolean[vars];	// assumed initialized by falses;
		boolean var = bexprtree.evaluate(falses);
		
		if(verbose!=-1){
			if (var==true){
				System.out.println("NDR_l > NDR_d");
			}else{
				System.out.println("NDR_l < NDR_d");
			}
		}
		
		// step 4
		// sort
		// Y (Xn) = Sort(Y (Xn)),
		expr.sort();
		if(verbose == 1)
			System.out.println("After step 1 (sorting):      "+expr.toString());
		
		// step 5
		// set i = 1, and j = 2,
		// we enumerate from 0...
		int i=0,j=1;
		if(verbose == 1)
			System.out.println("After step 2 (setting):      i="+(i+1)+" j="+(j+1));
		
		// if count==1 nothing to do
		if(expr.count()<2){
			return expr;
		}
		do{
			k++;
			// step 6
			String s_ni = expr.xors.ors.get(i).toString();
			String s_nj = expr.xors.ors.get(j).toString();
			String s_and="("+s_ni+")*("+s_nj+")";
			BExprTree bet_and,bet_ni,bet_nj;
			try {
				bet_and = new BExprTree(s_and);
				// reduces expression
				bet_and = bet_and.SOPReducer();
				bet_ni = new BExprTree(s_ni);
				bet_nj = new BExprTree(s_nj);
				
				if(!bet_and.compareExprTo(bet_ni) && !bet_and.compareExprTo(bet_nj)){
					if(verbose == 1)
						System.out.println("After step 3 (if): true");
					// step 7
					// simplify
					BExprTree bet_or = new BExprTree("("+s_ni+")+("+s_nj+")");
					
					
					//expr.xors.ors.set(i, new GTGOr(bet_or.SOPReducerToString()));
					expr.xors.ors.set(i, new GTGOr(bet_or.toString()));
					
					// step 8
					//expr.xors.ors.set(j,new GTGOr(bet_and.SOPReducerToString()));
					expr.xors.ors.set(j,new GTGOr(bet_and.toString()));
					
					if(verbose == 1)
						System.out.println("After step 4.1 and 4.2 (setting N_i N_j): "+expr.toString());
					// step 9
					// simplify
					expr.simplifier();
					if(verbose == 1)
						System.out.println("After step 4.3 (simplifying):  "+expr.toString());
					
					// step 10
					// Y (Xn) = Sort(Y (Xn))
					expr.sort();
					if(verbose == 1)
						System.out.println("After step 4.4 (sorting):      "+expr.toString());
					// step 11
				}else{
					if(verbose == 1)
						System.out.println("After step 3 (if): false");
					// step 12
					j++;
					if(verbose == 1)
						System.out.println("After step 5 (incrementing j):   i="+(i+1)+" j="+(j+1));					
					//step 13
				}
				i_count = expr.count();
				// step 14
				// if j > Count(Y (Xn)) then i = i + 1, j = i + 1,
				if(j+1>i_count){
					i++;
					j=i+1;
					if(verbose == 1)
						System.out.println("After step 6 (if): true, setting i,j");//: i="+(i+1)+" j="+(j+1));
				}else{
					if(verbose == 1)
						System.out.println("After step 6 (if): false");
				}
			} catch (BExprPreParseException e) {
				e.printStackTrace();
			}
			// step 15
			// if i < Count(Y (Xn)) then goto 6-th step
			if(verbose == 1)
				System.out.println("i="+(i+1)+" j="+(j+1)+" count="+i_count);			
			if(i+1<i_count){
				if(verbose == 1)
					System.out.println("After step 7 (goto 3)");
			}else{
				nextloop=false;
				if(verbose == 1)
					System.out.println("End of synthesis");
			}
		}while(nextloop);
		
		expr.sortExpression();
		
		return expr;
	}
	
	/**
	 * Sorts expression for better readability
	 */
	public void sortExpression(){
		Iterator<GTGOr> orIt = this.xors.ors.iterator();
		while(orIt.hasNext()){
			GTGOr or = orIt.next();
			Iterator<GTGAnd> andIt = or.ands.iterator();
			while(andIt.hasNext()){
				GTGAnd and = andIt.next();
				Collections.sort(and.variables);
			}
			/*
			 * Comparator needs sorted variables inside ands to work correctly
			 */
			Collections.sort(or.ands, new GTGAnd.GTGAndStringComparator());
		}
	}
	
	/**
	 * Simplifies xorgs expression by eliminating the same elements.
	 * ( a xor a xor c = c )
	 */
	public void simplifier() {
		//contains expression trees
		ArrayList<BExprTree> expressionTrees = new ArrayList<BExprTree>();
		
		//saving expression trees
		for(int i = 0; i < xors.ors.size(); i++){
			GTGOr or= xors.ors.get(i);
			BExprTree bet_or = null;
			try {
				bet_or = new BExprTree(or.toString());
			} catch (BExprPreParseException e) {
				e.printStackTrace();
			}
			expressionTrees.add(bet_or);
		}
		
		//checking
		for(int i = 0; i < xors.ors.size(); i++){
			BExprTree bet_or1 = expressionTrees.get(i);

			for(int j=i+1; j<xors.ors.size(); j++ ){
				BExprTree bet_or2 = expressionTrees.get(j);
				if(bet_or1.compareTo(bet_or2)){
					//removing from list of xors
					xors.ors.remove(j);
					xors.ors.remove(i);
					//removing from expressionTrees
					expressionTrees.remove(j);
					expressionTrees.remove(i);
					i=0;
					break;
				}
			}
		}
	}

	/**
	 * Implements "Count" function from documentation
	 * @return the number of XOR terms in expression
	 */
	public int count(){
		return xors.ors.size();
	}
	
	/**
	 * Implements "Sort" function from documentation
	 * Modifies expression to be ordered accordingly to the smallest number of variables in products and the biggest number of terms in a sum. 
	 */
	public void sort(){
		Collections.sort(this.xors.ors, new GTGOr());
		return;
	}
	
	/**
	 * Returns reduced expression in form of sum of products
	 * @param expression - expression
	 */
	public static String SOPReducerToString(String expression){
		BExprTree bexprtree = null;
		try {
			bexprtree = new BExprTree(expression);
		} catch (BExprPreParseException e) {
			return "Error";
			//e.printStackTrace();
		}
		return bexprtree.SOPReducerToString();
	}
	/**
	 * Returns reduced expression in form of product of sums
	 * @param expression - expression
	 */
	public static String POSReducerToString(String expression){
		BExprTree bexprtree = null;
		try {
			bexprtree = new BExprTree(expression);
		} catch (BExprPreParseException e) {
			return "Error";
			//e.printStackTrace();
		}
		return bexprtree.POSReducerToString();
	}
}
